package com.xc.quickstart.manager.sso;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.ImmutableMap;
import com.plumelog.core.util.LogExceptionStackTrace;
import com.xc.quickstart.admin.query.LogoutQuery;
import com.xc.quickstart.admin.query.SSOQuery;
import com.xc.quickstart.api.query.LoginQuery;
import com.xc.quickstart.api.vo.UserInfoVO;
import com.xc.quickstart.common.constant.RedisKeyConstant;
import com.xc.quickstart.common.context.ApiContext;
import com.xc.quickstart.common.exception.CommonBizException;
import com.xc.quickstart.common.model.ApiResponse;
import com.xc.quickstart.common.util.RestTemplateUtil;
import com.xc.quickstart.infrastructure.redis.RedisService;
import com.xc.quickstart.manager.oa.model.FeignOaAdminResponseModel;
import com.xc.quickstart.manager.oa.model.FeignOaDepartmentResponseModel;
import com.xc.quickstart.manager.oa.model.OaAdminModel;
import com.xc.quickstart.manager.oa.model.OaDepartmentModel;
import com.xc.quickstart.manager.sso.model.*;
import com.xc.quickstart.service.dto.DeptInfoDTO;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.Instant;
import java.util.*;

@Component
@Slf4j
public class SsoManager {

    @Autowired
    private SSOClient ssoClient;

    @Autowired
    private AdminSSOClient adminSSOClient;

    @Autowired
    private RedisService redisService;


    //TODO nacos 配置SSO信息
    @Value("${sso.root_path}")
    private String ssoUrl;

    @Value("${sso.api_app_id}")
    private String apiAppId;

    @Value("${sso.api_app_secret}")
    private String apiAppSecret;

    private static final String ERROR_MSG = "error";

    private static final String SUCCESS = "ok";

    private static final String DEFAULT_KEY = "XCKJOPENADMINZNB";

    public ApiResponse logout(LogoutQuery query) {
        adminSSOClient.logout(query);
        return ApiResponse.OK;
    }

    public boolean checkAuthority(Integer userId, String url) {
        String r = redisService.hget(String.format(RedisKeyConstant.SSO_AUTHORITY_KEY, userId), url);
        return StringUtils.isNotBlank(r);
    }

    public boolean checkRoleAuth(Integer userId) {
        String r = redisService.get(String.format(RedisKeyConstant.SSO_AUTHORITY_ROLE_KEY, userId));
        return StringUtils.isNotBlank(r);
    }

    public boolean checkToken(SSOQuery query) {
        try {
            return adminSSOClient.checkToken(query).ok();
        } catch (Exception e) {
            log.warn("[SsoManager]校验token异常, e={}", LogExceptionStackTrace.erroStackTrace(e));
            return true;
        }
    }

    public ApiResponse getUserMenu(SSOQuery query) {
        FeignSSOResponseModel<UserAuthorityModel> response = adminSSOClient.getUserMenu(query);
        if (!response.ok()) {
            log.error("[SsoManager]用户菜单权限接口请求失败 token={}", query.getToken());
            return new ApiResponse(500, "用户菜单权限接口请求失败：" + response.getErrmsg());
        }
        Integer userId = ApiContext.getApiContextModel().getUserId();
        List<SSOMenu> menuList = formatRouterMenu(response.getRow().getPrivilegeList());
        List<String> buttonList = new ArrayList<>();
        formatRouterButton(response.getRow().getPrivilegeList(), buttonList);
        for (SSOMenu ssoMenu : menuList) {
            redisService.hset(String.format(RedisKeyConstant.SSO_AUTHORITY_KEY, userId), ssoMenu.getPath(), ssoMenu.getPath());
        }
        for (String button : buttonList) {
            redisService.hset(String.format(RedisKeyConstant.SSO_AUTHORITY_KEY, userId), button, button);
        }
        List<Integer> roleList = response.getRow().getRoleList();
        redisService.set(String.format(RedisKeyConstant.SSO_AUTHORITY_ROLE_KEY, userId), String.valueOf(roleList.size()));
        return ApiResponse.ok(ImmutableMap.of("menuList", menuList, "buttonList", buttonList));
    }

    private List<SSOMenu> formatRouterMenu(List<PrivilegeInfo> priInfos) {
        List<SSOMenu> routerMenuList = new ArrayList<>();
        if (!org.springframework.util.CollectionUtils.isEmpty(priInfos)) {
            for (PrivilegeInfo p : priInfos) {
                if (p.getType().equalsIgnoreCase("menu")) {
                    SSOMenu routerMenu = new SSOMenu();
                    SSOMenuMeta menuMeta = new SSOMenuMeta();
                    menuMeta.setHideInMenu(p.getIs_hide() == null ? true : p.getIs_hide() == 0 ? false : true);
                    menuMeta.setTitle(p.getName());
                    menuMeta.setIcon("md-clipboard");
                    routerMenu.setMeta(menuMeta);
                    routerMenu.setId(p.getResource_id().intValue());
                    routerMenu.setPid(p.getParent_id().intValue());
                    routerMenu.setName(p.getName());
                    routerMenu.setPath(p.getUri());
                    routerMenu.setComponent(p.getP_key());
                    routerMenu.setChildren(formatRouterMenu(p.getChildren()));
                    routerMenuList.add(routerMenu);
                }
            }
        }
        return routerMenuList;
    }

    private void formatRouterButton(List<PrivilegeInfo> priInfos, List<String> buttonList) {
        if (!org.springframework.util.CollectionUtils.isEmpty(priInfos)) {
            for (PrivilegeInfo p : priInfos) {
                if (p.getType().equalsIgnoreCase("button")) {
                    buttonList.add(p.getUri());
                    formatRouterButton(p.getChildren(), buttonList);
                }
            }
        }
    }

    public ApiResponse getSimpleUserInfo(SSOQuery query) {
        FeignSSOResponseModel<SSOSimpleUserInfo> response = adminSSOClient.getSimpleUserInfo(query);
        if (!response.ok()) {
            log.error("[SsoManager]获取SimpleUserInfo失败 token={}", query.getToken());
            return ApiResponse.fail(response.getErrmsg());
        }

        return ApiResponse.ok(response.getRow());
    }

    public List<DimissionUserModel> getDimissionUserModelList() {
        Instant start = Instant.now();

        log.info("[SsoManager]获取离职员工信息开始,crmTraceId:{}", ApiContext.getCrmTraceId());
        FeignSSOResponseModel<List<DimissionUserModel>> result = ssoClient.getDimissionUser(new SSOPageModel());
        log.info("[SsoManager]获取离职员工信息结束,result:{},耗时:{}ms,crmTraceId:{}"
                , JSON.toJSONString(result.getRows())
                , Duration.between(start, Instant.now()).toMillis()
                , ApiContext.getCrmTraceId());

        if (!ERROR_MSG.equals(result.getResult())) {
            return result.getRows();
        }

        return Collections.emptyList();
    }

    public List<SSOUserByCodeModel> getSSOUserByCodeModelList(String code) {
        Instant start = Instant.now();

        log.info("[SsoManager]获取权限人员列表数据,crmTraceId:{}", ApiContext.getCrmTraceId());
        FeignSSOResponseModel<List<SSOUserByCodeModel>> result = ssoClient.queryUserByResourceCode(code);
        log.info("[SsoManager]获取权限人员列表数据,result:{},耗时:{}ms,crmTraceId:{}"
                , JSON.toJSONString(result.getRows())
                , Duration.between(start, Instant.now()).toMillis()
                , ApiContext.getCrmTraceId());

        if (!ERROR_MSG.equals(result.getResult())) {
            return result.getRows();
        }
        return Collections.emptyList();
    }


    /**
     * 非递归获取部门下的员工
     */
    public List<OaAdminModel> getOaAdminModelList(String deptId) {
        String url = ssoUrl.concat("/private/inner/employee/getEmpByDeptId?deptId=").concat(deptId);

        HttpHeaders headers = new HttpHeaders();
        headers.add("appId", apiAppId);
        headers.add("secret", apiAppSecret);

        String response = RestTemplateUtil.getJson(url, headers, null);
        return wrapToOaAdminModelList(response);
    }

    /**
     * 非递归的获取离职员工
     */
    public List<OaAdminModel> getLeaveOaAdminModelList(String startTime, String endTime) {
        Map<String, String> urlParam = new HashMap<>(8);
        urlParam.put("begin", startTime);
        urlParam.put("end", endTime);

        HttpHeaders headers = new HttpHeaders();
        headers.add("appId", apiAppId);
        headers.add("secret", apiAppSecret);

        String response = RestTemplateUtil.getJson(ssoUrl.concat("/private/inner/employee/getLeanedemp"), headers, JSON.toJSONString(urlParam));
        return wrapToOaAdminModelList(response);
    }


    public String getSmsCode(String mobile) {
        Instant start = Instant.now();
        log.info("[SsoManager]获取验证码,crmTraceId:{}", ApiContext.getCrmTraceId());
        FeignSSOResponseModel result = ssoClient.getSmsCode(mobile);
        log.info("[SsoManager]获取验证码,result:{},耗时:{}ms,crmTraceId:{}"
                , JSON.toJSONString(result.getRows())
                , Duration.between(start, Instant.now()).toMillis()
                , ApiContext.getCrmTraceId());

        if (ObjectUtils.isEmpty(result) || !SUCCESS.equals(result.getResult())) {
            log.warn("[获取验证码失败]手机号: {},验证结果: {}", mobile, JSON.toJSONString(result));
            return result.getErrmsg();
        }
        return null;
    }

    public UserInfoVO loginWithSmsCode(LoginQuery loginDTO) {
        Instant start = Instant.now();
        log.info("[SsoManager]登录,crmTraceId:{}", ApiContext.getCrmTraceId());
        Object result = ssoClient.login(loginDTO);
        FeignSSOResponseModel<UserInfoVO> feignSSOResponseModel = JSON.parseObject(JSON.toJSONString(result), FeignSSOResponseModel.class);
        log.info("[SsoManager]登录,result:{},耗时:{}ms,crmTraceId:{}"
                , JSON.toJSONString(feignSSOResponseModel.getRows())
                , Duration.between(start, Instant.now()).toMillis()
                , ApiContext.getCrmTraceId());

        if (ObjectUtils.isEmpty(result) || ObjectUtils.isEmpty(feignSSOResponseModel.getRow()) || !SUCCESS.equals(feignSSOResponseModel.getResult())) {
            log.warn("[获取验证码失败]手机号: {},验证结果: {}", loginDTO.getMobile(), JSON.toJSONString(result));
            throw new CommonBizException(feignSSOResponseModel.getErrmsg());
        }
        return JSON.parseObject(JSON.toJSONString(feignSSOResponseModel.getRow()), UserInfoVO.class);
    }

    /**
     * 获取所有部门信息
     */
    public List<OaDepartmentModel> getOaDepartmentModelList() {
        HttpHeaders headers = new HttpHeaders();
        headers.add("appId", apiAppId);
        headers.add("secret", apiAppSecret);

        String response = RestTemplateUtil.getJson(ssoUrl.concat("/private/inner/dept/getAllDepartment"), headers, null);

        FeignOaDepartmentResponseModel feignOaDepartmentResponseModel = JSON.parseObject(response, FeignOaDepartmentResponseModel.class);
        if (feignOaDepartmentResponseModel.getCode().equals(ApiResponse.OK_CODE) && CollectionUtils.isNotEmpty(feignOaDepartmentResponseModel.getRows())) {
            return feignOaDepartmentResponseModel.getRows();
        }

        return Collections.emptyList();
    }

    public UserAuthorityModel getUserAuthorityModel(String jobNumber) {
        SSOUserInfoReq ssoUserInfoReq = new SSOUserInfoReq(jobNumber, apiAppId);
        FeignSSOResponseModel<UserAuthorityModel> result = ssoClient.getUserAuthorityModel(ssoUserInfoReq);
        return result.getRow();
    }

    public List<DeptInfoDTO> getDeptInfoDTOList(String jobNumber) {
        FeignSSOResponseModel<List<DeptInfoDTO>> result = ssoClient.getDeptInfoDTOList(jobNumber);
        return result.getRows();
    }

    private List<OaAdminModel> wrapToOaAdminModelList(String response) {
        FeignOaAdminResponseModel feignOaAdminResponseModel = JSON.parseObject(response, FeignOaAdminResponseModel.class);

        if (ObjectUtils.isEmpty(feignOaAdminResponseModel.getCode())) {
            return Collections.emptyList();
        }

        if (!feignOaAdminResponseModel.getCode().equals(ApiResponse.OK_CODE) || CollectionUtils.isEmpty(feignOaAdminResponseModel.getRows())) {
            return Collections.emptyList();
        }

        List<OaAdminModel> oaAdminModelList = feignOaAdminResponseModel.getRows();
        oaAdminModelList.forEach(item -> item.setMobile(decode(item.getMobile())));
        return oaAdminModelList;
    }


    @SneakyThrows
    private static String decode(String content) {
        byte[] arr = Base64.decodeBase64(content);
        byte[] raw = DEFAULT_KEY.getBytes(StandardCharsets.UTF_8);
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, skeySpec);
        byte[] original = cipher.doFinal(arr);
        return new String(original, StandardCharsets.UTF_8);
    }

    /**
     * 获取部门权限人员
     *
     * @param deptIdList
     * @return
     */
    public List<DeptDataPermissionModel> listAllDataPermissionUserByDeptIds(List<Long> deptIdList) {
        DeptDataPermissionQuery deptDataPermissionQuery = new DeptDataPermissionQuery();
        deptDataPermissionQuery.setAppId("crm");
        deptDataPermissionQuery.setDeptIds(deptIdList);

        FeignSSOResponseModel<List<DeptDataPermissionModel>> listFeignSSOResponseModel = ssoClient.listAllDataPermissionUserByDeptIds(deptDataPermissionQuery);
        if (CollectionUtils.isEmpty(listFeignSSOResponseModel.getRows())) {
            return Collections.emptyList();
        }

        return listFeignSSOResponseModel.getRows();
    }
}
